package com.he.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.he.dto.Model;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.regex.Pattern;

@SuppressWarnings("all")
public class AppUtil {

    private static final String FILE_SYSTEM_PREFIX =ApplicationProperty.getFileSystemManagerDest();
    private static Logger logger= LoggerFactory.getLogger(AppUtil.class);
    private static final Pattern INSECURE_URI = Pattern.compile(ApplicationProperty.getValueByKey("app.error.url"));

    /**
     * 判断请求是否合法
     * @param request
     */
    public static boolean isAccess(ChannelHandlerContext ctx,FullHttpRequest request,String uri)throws Exception{

        String path=null;
        if(request.decoderResult().isFailure()){    //解码是否成功
            sendOver(ctx,HttpResponseStatus.BAD_REQUEST);
        }else if (request.method() !=HttpMethod.GET){   //是否是get请求
            sendOver(ctx,HttpResponseStatus.METHOD_NOT_ALLOWED);
        }else if ((path=convertUrlToLocalPath(request,uri))==null){//文件路径是否合法
            logger.debug(path);
            sendOver(ctx,HttpResponseStatus.FORBIDDEN);
        }else {//文件是否存在
            File file=new File(path);
            if(file.isHidden() || !file.exists()){
                sendOver(ctx,HttpResponseStatus.NOT_FOUND);
                return false;
            }
            return true;
        }
        return false;

    }

    /**
     * 服务端结束此次访问
     */
    public static void sendOver(ChannelHandlerContext ctx, HttpResponseStatus status)throws Exception{
        logger.debug("用户访问失败,原因是：{}",status);
        ObjectMapper mapper=new ObjectMapper();
        Model model=new Model().setState(500).setDescribe(status.toString());
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(mapper.writeValueAsString(model), CharsetUtil.UTF_8));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json;charset=UTF-8");
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    /**
     * 将url翻译为本地文件路径
     * @param request
     * @return
     * @throws Exception
     */
    public static String convertUrlToLocalPath(FullHttpRequest request,String uri)throws Exception{

        logger.debug("用户访问的url是：{}",uri);
        if(uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".") || INSECURE_URI.matcher(uri).matches()){
            return null;
        }else {
            String path=FILE_SYSTEM_PREFIX+uri;
            logger.debug("本地文件是：{}",path);
            return path;
        }
    }

    /**
     * 将文件夹目录显示为网页
     * @param ctx
     * @param dir
     * @throws UnsupportedEncodingException
     */
    public static void sendListing(ChannelHandlerContext ctx, File dir) throws UnsupportedEncodingException {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().set("CONNECT_TYPE", "text/html;charset=UTF-8");
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");

        String dirPath = new String(dir.getPath().getBytes(),"utf-8");
        logger.debug("系统发布的路径是："+dirPath);
        StringBuilder buf = new StringBuilder();

        buf.append("<!DOCTYPE html>\r\n");
        buf.append("<html><head><title>");
        buf.append(dirPath);
        buf.append("目录:");
        buf.append("</title></head><body>\r\n");
        buf.append("<h3>");
        buf.append(dirPath).append(" 目录：");
        buf.append("</h3>\r\n");
        buf.append("<ul>");
        for (File f : dir.listFiles()) {
            String name = new String(f.getName().getBytes(),"utf-8");
            logger.debug("该路径下每个文件的路径是："+name);
            buf.append("<li>链接：<a href=\"");
            buf.append(name);
            buf.append("\">");
            buf.append(name);
            buf.append("</a></li>\r\n");
            buf.append("<br/>");
        }
        buf.append("</ul></body></html>\r\n");

        ByteBuf buffer = Unpooled.copiedBuffer(buf,CharsetUtil.UTF_8);
        response.content().writeBytes(buffer);
        buffer.release();
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }


    /**
     * 将单个文件写出到客户端
     * @param ctx
     * @param request
     * @param file
     * @throws Exception
     */
    public static void writeToUser(ChannelHandlerContext ctx ,FullHttpRequest request,File file)throws Exception{
        logger.debug("文件写出开始：{}",file.getAbsolutePath());
        RandomAccessFile randomAccessFile=new RandomAccessFile(file,"r");
        long fileLength = randomAccessFile.length();

        // 响应头
        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        HttpUtil.setContentLength(response, fileLength);
        MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimetypesFileTypeMap.getContentType(file.getPath()));
        if(HttpUtil.isKeepAlive(request))response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        ctx.write(response);

        //大文件写出时，此时是保持连接 这里在慢慢写 ing……
        ChannelFuture sendFileFuture = ctx.write(new ChunkedFile(randomAccessFile, 0, fileLength, 8192), ctx.newProgressivePromise());
        sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
            public void operationComplete(ChannelProgressiveFuture future) throws Exception {
                logger.debug("文件写出完成：{}",file.getAbsolutePath());
            }
            public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception {
                if(total < 0) logger.debug("分批次写出的大小是：{}",progress);
                else logger.debug("导出时的进程是开始：{}，文件大小：{}",progress,total);
            }
        });

        //写出完成
        ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        lastContentFuture.addListener(ChannelFutureListener.CLOSE);
        logger.debug("写出完成……");

    }

    /**
     * 重定向
     * @param ctx
     * @param newUri
     */
    public static void sendRedirect(ChannelHandlerContext ctx, String newUri){
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FOUND);
        response.headers().set(HttpHeaderNames.LOCATION, newUri);
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

}



